package com.wesley.schedule.utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * SQL变量替换工具
 */
public class VarsUtil {
    /**
     * 匹配变量的正则 ${now.now().delta(day=-1).datekey}
     */
    private static final String expression = "\\$now\\.([a-zA-Z]|[0-9\\.\\-_]|[\\(\\)=]){4,60}";
    private static final Pattern varsParser = Pattern.compile(expression); // 正则表达式

    private static final SimpleDateFormat sdfYear = new SimpleDateFormat("yyyy");
    private static final SimpleDateFormat sdfYearmo = new SimpleDateFormat("yyyyMM");
    private static final SimpleDateFormat sdfYearmoLine = new SimpleDateFormat("yyyy-MM");
    private static final SimpleDateFormat sdfYearWeekNum = new SimpleDateFormat("w");
    private static final SimpleDateFormat sdfWeek = new SimpleDateFormat("E");
    private static final SimpleDateFormat sdfDatekey = new SimpleDateFormat("yyyyMMdd");
    private static final SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
    private static final SimpleDateFormat sdfHour = new SimpleDateFormat("H");
    private static final SimpleDateFormat sdfZHour = new SimpleDateFormat("HH");
    private static final SimpleDateFormat sdfMinute = new SimpleDateFormat("m");
    private static final SimpleDateFormat sdfNow = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    //基础关键词
    private static final String NOW = "$now";

    //计算类型列表KE
    private static final String DELTA = "delta";

    /**
     * 使用当前系统时间 解析变量
     * @param var 变量
     */
    public static String parseDateVar(String var){
        return parseDateVar(var, System.currentTimeMillis());
    }

    /**
     * 解析变量方法
     * @param var 变量
     * @param ts 时间戳毫秒数
     */
    public static String parseDateVar(String var, Long ts) {
        // var为空，直接返回
        if(Objects.isNull(var) || var.isEmpty()){
            return "";
        }

        // 填充默认参数时间戳
        if(Objects.isNull(ts)){
            ts = System.currentTimeMillis();
        }

        String[] subStr = var.split("\\.");
        List<String> subList = new ArrayList<>(Arrays.asList(subStr));
        String start = subList.remove(0);
        if(NOW.equals(start)) {
            return recursionVar(subList, ts);
        }

        throw new RuntimeException("变量必须以$now开始, var: " + var);
    }

    /**
     * 递归解析
     * @param vars 变量列表
     * @param ts 时间戳
     */
    private static String recursionVar(List<String> vars, Long ts) {
        if (vars.size() > 1) {
            // 当大于1时，递归
            String var = vars.remove(0);

            //中间解析
            Long parseDateTs = parseMiddle(var, ts);

            return recursionVar(vars, parseDateTs);
        } else if (vars.size() == 1) {
            // 当等于1时，递归结束
            String var = vars.remove(0);
            return parseLast(var, ts);
        } else {
            throw new RuntimeException("递归参数vars集合数量等于0！");
        }
    }

    /**
     * 处理中间解析逻辑
     * @param var 变量
     * @param ts 时间戳
     */
    private static Long parseMiddle(String var, long ts) {
        Long res = null;
        Date odate = new Date(ts);

        switch (var){
            case "now()":
                res = System.currentTimeMillis();
                break;
            case "week_begin_date":
                Date weekBeginDate = getWeek(odate,0);
                res = weekBeginDate.getTime();
                break;
            case "week_end_date":
                Date weekEndDate = getWeek(odate,1);
                res = weekEndDate.getTime();
                break;
            case "month_begin_date":
                Date monthBeginDate = getMonth(odate,0);
                res = monthBeginDate.getTime();
                break;
            case "month_end_date":
                Date monthEndDate = getMonth(odate,1);
                res = monthEndDate.getTime();
                break;
            default:
                if(var.startsWith(DELTA)){
                    //处理delta逻辑，会根据需求，变换天，周，月的变化值
                    res = processDelta(var,ts);
                }else {
                    throw new RuntimeException("没有对应的时间转换方式，对于key：" + var);
                }
                break;
        }

        return res;
    }

    /**
     * 处理delta逻辑，会根据需求，变换天，周，月的变化值
     * @param var 变量
     * @param ts 时间戳
     * @return
     */
    public static Long processDelta(String var, long ts) {
        Calendar calendar = Calendar.getInstance();
        Date tsDate = new Date(ts);
        calendar.setTime(tsDate);

        String deltaCondition = var.substring(var.indexOf("(") + 1, var.indexOf(")"));
        if (deltaCondition.contains("=")) {
            String[] condition = deltaCondition.split("=");
            String deltaType = condition[0].trim();
            int deltaNum = Integer.parseInt(condition[1].trim());
            switch (deltaType) {
                case "min":
                    calendar.add(Calendar.MINUTE, -deltaNum);
                    break;
                case "hour":
                    calendar.add(Calendar.HOUR, -deltaNum);
                    break;
                case "day":
                    calendar.add(Calendar.DATE, -deltaNum);
                    break;
                case "week":
                    deltaNum = deltaNum * 7;
                    calendar.add(Calendar.DATE, -deltaNum);
                    break;
                case "month":
                    calendar.add(Calendar.MONTH, -deltaNum);
                    break;
                default:
                    throw new RuntimeException("日期delta类型不支持：" + var);
            }
        } else {
            int deltaNum = Integer.parseInt(deltaCondition);
            calendar.add(Calendar.DATE, -deltaNum);
        }
        Date date = calendar.getTime();
        return date.getTime();
    }

    /**
     * 处理最后一层解析逻辑
     * @param var 变量
     * @param ts 时间戳
     */
    private static String parseLast(String var, Long ts) {
        String res = null;

        //解析逻辑
        Date date = new Date(ts);
        switch (var){
            case "year":
                res = sdfYear.format(date);
                break;
            case "yearmo":
                res = sdfYearmo.format(date);
                break;
            case "year_mo":
                res = sdfYearmoLine.format(date);
                break;
            case "datekey":
                res = sdfDatekey.format(date);
                break;
            case "date":
                res = sdfDate.format(date);
                break;
            case "year_week_num":
                res = sdfYear.format(date) + "WK" + sdfYearWeekNum.format(date);
                break;
            case "week":
                res = sdfWeek.format(date);
                break;
            case "hour":
                res = sdfHour.format(date);
                break;
            case "zhour":
                res = sdfZHour.format(date);
                break;
            case "minute":
                res = sdfMinute.format(date);
                break;
            case "now()":
                res = sdfNow.format(date);
                break;
            case "timestamp":
                res = ts/1000+"";
                break;
            case "timemillis":
                res = ts+"";
                break;
            default:
                throw new RuntimeException("变量书写不规范，请参照规范文档！变量：" + var);
        }
        return res;
    }

    /**
     * 获取本周开始和结束时间
     * @param date
     * @return
     */
    private static Date getWeek(Date date,int type) {
        // 使用Calendar类进行时间的计算
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        // 1.获得当前日期是一个星期的第几天 2.判断要计算的日期是否是周日，如果是则减一天计算周六的，否则会计算到下一周去。 3.dayWeek值（1、2、3...）对应周日，周一，周二...
        int dayWeek = cal.get(Calendar.DAY_OF_WEEK);
        if (dayWeek == 1) {
            dayWeek = 7;
        } else {
            dayWeek -= 1;
        }

        // 计算本周开始的时间
        cal.add(Calendar.DAY_OF_MONTH, 1 - dayWeek);

        if(0==type){
            return cal.getTime();
        }else {
            cal.add(Calendar.DATE,6);
            return cal.getTime();
        }
    }

    /**
     * 获取本月的开始和结束时间
     * @param date
     * @return
     */
    private static Date getMonth(Date date,int type) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        if(0==type) {
            // 设置为1号,当前日期既为本月第一天
            cal.set(Calendar.DAY_OF_MONTH, 1);
            return cal.getTime();
        }else {
            cal.add(Calendar.MONTH, 1);
            cal.set(Calendar.DAY_OF_MONTH, 1);
            cal.add(Calendar.DATE,-1);
            return cal.getTime();
        }
    }

    /**
     * 替换路由规则里面的动态参数
     * @param routeConfigStr 配置参数
     */
    public static String replaceDynParams(String routeConfigStr){
        return replaceDynParams(routeConfigStr,0L);
    }

    /**
     * 替换路由规则里面的动态参数，datekey:yyyyMMdd
     * @param routeConfigStr 配置参数
     */
    public static String replaceDynParams(String routeConfigStr, String dateKey) throws ParseException {
        Date date = sdfDatekey.parse(dateKey.replace("'", ""));
        return replaceDynParams(routeConfigStr, date.getTime());
    }

    /**
     * 替换路由规则里面的动态参数，传入ts, 控制时间开始
     * @param routeConfigStr 配置参数
     */
    public static String replaceDynParams(String routeConfigStr, Long ts) {
        if(Objects.isNull(routeConfigStr) || routeConfigStr.isEmpty()){
            return "";
        }

        String res = routeConfigStr;
        Matcher m = varsParser.matcher(routeConfigStr);
        while (m.find()) {
            String var = routeConfigStr.substring(m.start(), m.end());
            if (var.isEmpty()) {
                continue;
            }

            if (ts != null && ts > 0L) {
                res = res.replace(var, parseDateVar(var, ts));
            } else {
                res = res.replace(var, parseDateVar(var));
            }
        }
        return res;
    }

}
